package com.tiancheng.trade.merchant.aop;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSON;
//import com.tiancheng.trade.merchant.exception.handler.ControllerExceptionHandler;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.InputStreamSource;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.TreeMap;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 * 统一日志处理切面
 */
@Aspect
@Component
@Slf4j
public class LogAspect {
    @Autowired
    @Qualifier("logExecutor")
    private Executor logExecutor;

    /**
     * controller层切点
     * 异常处理见
     *
     */
    @Pointcut("execution(public * com.tiancheng.trade.merchant..controller..*.*(..))")
    public void webLog() {
    }

    /**
     * 外部应用请求本应用前置日志处理
     *
     * @param point {@link JoinPoint}
     */
    @Before(value = "webLog()")
    public void doBeforeWebLog(JoinPoint point) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes()))
                .getRequest();

        TreeMap<String, Object> requestInfoMap = new TreeMap<>();
        requestInfoMap.put("clientHost", request.getRemoteAddr());
        requestInfoMap.put("requestMethod", request.getMethod());
        requestInfoMap.put("appUrl", request.getRequestURL().toString());
        requestInfoMap.put("appClass", point.getSignature().getDeclaringTypeName());
        requestInfoMap.put("classMethod", point.getSignature().getName());

        List<Object> params = Arrays.stream(point.getArgs())
                .filter(d -> !(d instanceof InputStreamSource) && !(d instanceof ServletRequest) && !(d instanceof ServletResponse)).collect(Collectors.toList());

        requestInfoMap.put("requestParameter", params.size() > 1 ?
                JSON.toJSONString(params) : (params.size() > 0 ? JSON.toJSONString(params.get(0)) : null));

        //requestInfoMap.put("userId", null == StpUtil.getLoginId() ? "" : StpUtil.getLoginId() );
        requestInfoMap.put("accessToken", request.getParameter("access_token"));

        // 健康检查不打印日志
        if ((requestInfoMap.get("appUrl") + "").contains("readinessProbe")) {
            return;
        }

        logExecutor.execute(() -> log.info(JSON.toJSONString(requestInfoMap)));
    }

    /**
     * 外部应用请求本应用后置日志处理
     *
     * @param rt {@link Object}
     */
    @AfterReturning(value = "webLog()", returning = "rt")
    public void AfterReturningWebLog(Object rt) {
        String appUrl = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes()))
                .getRequest().getRequestURL().toString();

        // 健康检查不打印日志
        if (appUrl.contains("readinessProbe")) {
            return;
        }

        if (rt != null) {
            logExecutor.execute(() -> log.info("[" + appUrl + "] result: {}", JSON.toJSONString(rt)));
        }
    }

    /**
     * redis、feign切点
     */
    @Pointcut("execution(public * org.springframework.web.client.RestTemplate.*(..)) " +
            "|| execution(public * com.tiancheng.trade.merchant..feign.*.*(..))")
    public void requestLog() {
    }

    /**
     * redis、feign异常日志处理
     *
     * @param ex {@link Exception}
     */
    @AfterThrowing(value = "requestLog()", throwing = "ex")
    public void doAfterThrowingRequestLog(Exception ex) {
        logExecutor.execute(() -> log.error("redis or feign error: {}", ex.getMessage()));
    }

    /**
     * redis、feign后置日志处理
     *
     * @param rt {@link Object}
     */
    @AfterReturning(value = "requestLog()", returning = "rt")
    public void doAfterReturningRequestLog(Object rt) {
        if (rt != null) {
            logExecutor.execute(() -> log.info("result: {}", JSON.toJSONString(rt)));
        }
    }

}
